ShaderSticker = {
  ALL_SHADERSTICKER={},
  faceAnchorPoint = 29
}
ShaderSticker.__index = ShaderSticker

function ShaderSticker:new(sampler, samplerWidth, samplerHeight, stickerInfo)
  local newObject = {}
  setmetatable(newObject, ShaderSticker)

  stickerInfo = stickerInfo or {}

  newObject.sampler = sampler
  local aspectRatio = KuruEngine.getInstance():getResolution().x / KuruEngine.getInstance():getResolution().y
  local width = samplerWidth or newObject.sampler:getTexture():getWidth()
  local height = samplerHeight or newObject.sampler:getTexture():getHeight()
  newObject.stickerRatio = width / height

  newObject.baseMatrix = Matrix.createFromMatrix(Matrix.identity())
  newObject.baseMatrix:postScale(1.0, aspectRatio / newObject.stickerRatio, 1.0)

  local quadFullMesh = Mesh.createQuadFullscreen()
  local model = Model.create(quadFullMesh)
  quadFullMesh:release()

  local fragShader = stickerInfo.fragShader or "res/shaders/passthrough.frag"
  print(fragShader)

  newObject.material = Material.createWithShaderFile("res/shaders/passthrough.vert", fragShader, Nil)
  newObject.material:getParameter("u_worldViewProjectionMatrix"):setMatrix(newObject.baseMatrix)

  local blendSrc = stickerInfo.blendSrc or RenderStateBlend.BLEND_SRC_ALPHA
  local stateBlock = newObject.material:getStateBlock()
  stateBlock:setBlend(true)
  stateBlock:setBlendSrc(blendSrc)
  stateBlock:setBlendDst(RenderStateBlend.BLEND_ONE_MINUS_SRC_ALPHA)
  newObject.material:getParameter("u_texture"):setSampler(newObject.sampler)
  model:setMaterial(newObject.material, -1)
  newObject.material:release()
  newObject.node = KuruModelNode.createFromModel(model)
  model:release()

  newObject.scale = 1.0
  newObject.translateX = 0.0
  newObject.translateY = 0.0
  newObject.rotateZ_1 = 0.0
  newObject.rotateZ_2 = 0.0

  self.ALL_SHADERSTICKER[#self.ALL_SHADERSTICKER + 1] = newObject

  return newObject
end

function ShaderSticker:disableAllNodes()
  for i,v in ipairs(self.ALL_SHADERSTICKER) do
    v.node:setEnabled(false)
  end
end

function ShaderSticker:setFaceInfo(faceCenter, faceScale, faceRoll)
  self.faceCenter = faceCenter
  self.faceScale = faceScale
  self.faceRoll = faceRoll
end

function ShaderSticker:applyMatrix()
  local aspectRatio = KuruEngine.getInstance():getResolution().x / KuruEngine.getInstance():getResolution().y

  local snapshotMatrix = Matrix.createFromMatrix(self.baseMatrix)

  local scale = self.scale * self.faceScale
  snapshotMatrix:postScale(scale, scale, 1.0)
  snapshotMatrix:postScale(1.0, 1.0 / aspectRatio, 1.0)
  snapshotMatrix:postRotateZ(self.rotateZ_1)
  snapshotMatrix:postTranslate(self.translateX * self.faceScale, self.translateY * self.faceScale, 0.0)
  snapshotMatrix:postRotateZ(self.rotateZ_2)
  snapshotMatrix:postScale(1.0, aspectRatio, 1.0)

  snapshotMatrix:postScale(1.0, 1.0 / aspectRatio, 1.0)
  snapshotMatrix:postRotateZ(self.faceRoll * math.pi / 180)
  snapshotMatrix:postScale(1.0, aspectRatio, 1.0)

  snapshotMatrix:postTranslate(self.faceCenter.x * 2.0 - 1.0, self.faceCenter.y * 2.0 - 1.0, 0.0)

  self.material:getParameter("u_worldViewProjectionMatrix"):setMatrix(snapshotMatrix)
end

function ShaderSticker:updateFaceData()
  ShaderSticker:disableAllNodes()

  local kuruFaceDetector = KuruFaceDetectorExtension.cast(KuruEngine.getInstance():getExtension("KuruFaceDetector"))

  if 0 < kuruFaceDetector:getFaceCount() then
    local faceFeature = KaleFaceFeature.cast(kuruFaceDetector:getFace(0))
    local faceData = faceFeature:getFaceData()
    local faceCenterVec = Vector3.create((faceData:getUlseeVertexShape(ShaderSticker.faceAnchorPoint).x + 1.0) / 2.0, (faceData:getUlseeVertexShape(ShaderSticker.faceAnchorPoint).y + 1.0) / 2.0, 0.0);

    for i,v in ipairs(ShaderSticker.ALL_SHADERSTICKER) do
      v:setFaceInfo(faceCenterVec, faceFeature:getNormalizedFaceScale() * 2.0, faceData.relativeRoll)
      v:applyMatrix()
      v.node:setEnabled(true)
    end
  end
end
